﻿using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Collections;
using System.Reflection.Emit;

namespace Lib
{


    public class ProcessManager
    {


        #region 模拟控制台信号需要使用的api 和公共函数
        //参考 https://blog.csdn.net/xuejianxinokok/article/details/140484504/

        const int CTRL_C_EVENT = 0;

        [DllImport("kernel32.dll")]
        static extern bool GenerateConsoleCtrlEvent(int dwCtrlEvent, int dwProcessGroupId);

        [DllImport("kernel32.dll")]
        static extern bool SetConsoleCtrlHandler(IntPtr handlerRoutine, bool add);

        [DllImport("kernel32.dll")]
        static extern bool AttachConsole(int dwProcessId);

        [DllImport("kernel32.dll")]
        static extern bool FreeConsole();


        [DllImport("kernel32.dll")]
        public static extern Boolean AllocConsole();//显示控制台



        public static void KillOthers()
        {
            int myId = Process.GetCurrentProcess().Id;

            var processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
            var ps = Process.GetProcessesByName(processName);
            if (ps.Length > 1)
            {
                foreach (var p in ps)

                {
                    try
                    {
                        if (myId != p.Id)
                        {
                            p.Kill();
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                    }


                }
            }

        }


        public static int runningCount()
        {
            var processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
            var count = Process.GetProcessesByName(processName).Length;
            return count;
        }

        public static bool IsProcessRunning(string processName)
        {
            return Process.GetProcessesByName(processName).Length > 0;
        }


        public static bool IsProcessRunning(int pid)
        {
            try
            {
                Process.GetProcessById(pid);
            }
            catch (InvalidOperationException)
            {
                return false;
            }
            catch (ArgumentException)
            {
                return false;
            }
            return true;
        }



        public static int KillAll(string processName)
        {
            Process[] ps = Process.GetProcessesByName(processName);

            foreach (var p in ps)

            {
                try
                {
                    p.Kill();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }


            }
            return ps.Length;
        }

        #endregion

        #region 管理ffmpeg
        //  https://blog.csdn.net/weixin_30338743/article/details/97123619
        // ffmpeg
        //private const string PROCESS_FFMPEG = @"ffmpeg";
        //// 正在运行的ffmpeg进程id
        ////private const string FFMPEG_ARGS = @" -f gdigrab -i desktop -y -vf ""drawtext=text='Watermark':fontsize=24:fontcolor=red:x=10:y=10 "" ";
        //// 默认录取时间为 4小时= 4*60*60  4G 大小
        //private const string FFMPEG_ARGS = @" -y  -f gdigrab  -i desktop  -r 10  -t 14400  -fs  4096M  -b:v 1M  -vf ""scale=1024:-1,drawtext=fontfile=msyh.ttf:text='薛建新 %{localtime}':fontsize=12:fontcolor=red:x=1:y=1,drawtext=fontfile=msyh.ttf:text='%{localtime}':x=mod(100*t\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4 "" ";
        //private static int ffmpegPID;
        //private static string saveFileName;


        //public static string getSaveFileName()
        //{
        //    return saveFileName;
        //}

        //public static void RunFFmpeg(string filename)

        //{
        //    //https://mathewsachin.github.io/blog/2017/07/28/ffmpeg-pipe-csharp.html

        //    KillAll(PROCESS_FFMPEG);
        //    Process p = new Process();//建立外部调用线程
        //    p.StartInfo.FileName = PROCESS_FFMPEG;   //ffmpeg.exe的绝对路径
        //    // gdigrab是ffmpeg内置的屏幕录制设备，但是这个设备不能同时采集音频，于是又用到了后面的dshow
        //    // https://blog.csdn.net/weixin_30338743/article/details/97123619
        //    saveFileName = filename + ".mp4";
        //    string args = FFMPEG_ARGS+ saveFileName;
        //    p.StartInfo.Arguments = args;  //ffmpeg的参数
        //    p.StartInfo.UseShellExecute = false;           //是否使用操作系统shell启动
        //    //p.StartInfo.RedirectStandardInput = true;      //允许写入标准输入
        //    p.StartInfo.RedirectStandardError = true;      //重定向标准错误输出
        //    p.StartInfo.CreateNoWindow = true;             //不显示程序窗口
        //    p.ErrorDataReceived += new DataReceivedEventHandler(Output);
        //    p.Start();
        //    ffmpegPID = p.Id;                            //获取关联进程的唯一标识符
        //    p.BeginErrorReadLine();
        //}

        public static void StopFFmpeg()

        {
            string pName = "ff7scre";
            // 开始那几个api接口是用来模拟ctrl+c命令的。本来以为在停止录制的时候直接kill掉进程就好，结果导致生成的视频文件直接损坏了。
            // 手动使用ffmpeg.exe的时候发现ctrl+c可以直接结束录制并且不会损坏视频文件，于是采用这种方法，在点击停止按钮时模拟ctrl+c来退出ffmpeg
            // 发送的顺序很重要 参考 https://stackoverflow.com/questions/37747161/c-sharp-terminate-process-with-ctrl-c
            for (int count = 0; count < 3; count++)
            {
                var ps = Process.GetProcessesByName(pName);
                if (ps.Length == 0)
                {
                    break;
                }
                foreach (var p in ps)
                {
                    //Disable Ctrl-C handling for our program
                    SetConsoleCtrlHandler(IntPtr.Zero, true);
                    //Console.WriteLine("Arguments:{0}", p.StartInfo.Arguments.ToString());
                    if (p.Id > 0 && AttachConsole(p.Id))
                    {
                        GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
                        //Must wait here. If we don't and re-enable Ctrl-C handling below too fast, we might terminate ourselves.
                        p.WaitForExit(1000 * 10);
                        FreeConsole();
                    }
                    //Re-enable Ctrl-C handling or any subsequently started programs will inherit the disabled state.
                    SetConsoleCtrlHandler(IntPtr.Zero, false);
                }
            }

            KillAll(pName);

        }

        //private static void Output(object sendProcess, DataReceivedEventArgs output)

        //{
        //    if (!String.IsNullOrEmpty(output.Data))
        //        Debug.WriteLine(output.Data.ToString());
        //}



        //public static string GetSHA256(string file)
        //{
        //    using (var stream = new BufferedStream(File.OpenRead(file), 1024 * 1024 * 5))

        //    {
        //        SHA256Managed sha = new SHA256Managed();
        //        byte[] checksum = sha.ComputeHash(stream);
        //        return BitConverter.ToString(checksum).Replace("-", String.Empty).ToUpper();
        //    }
        //}

        //public static string GetMD5(string file)
        //{
        //    using (var stream = new BufferedStream(File.OpenRead(file), 1024*1024*5))

        //    {

        //        MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider();
        //        byte[] buffer = md5Provider.ComputeHash(stream);
        //        string result = BitConverter.ToString(buffer);
        //        result = result.Replace("-", "").ToUpper();;
        //        return result.ToUpper();
        //    }
        //}






        //public static bool IsFFmpegRunning()
        //{
        //    return Process.GetProcessesByName(PROCESS_FFMPEG).Length > 0;
        //}

        //public static void KillFFmpeg()
        //{
        //    KillAll(PROCESS_FFMPEG);
        //}

        #endregion

        #region 管理 主浏览器进程
        // 主浏览器
        private const string PROCESS_MAIN = "tauritest240709";
        public static void RunMain()
        {

            bool isProcessRunning = IsProcessRunning(PROCESS_MAIN);

            if (!isProcessRunning)
            {
                Process.Start(PROCESS_MAIN, "fromStart");
            }

        }
        public static int StopMain()
        {
            return KillAll(PROCESS_MAIN);
        }

        #endregion


    }

    public class Hook : IDisposable
    {
        public delegate int HookProc(int nCode, int wParam, IntPtr lParam);
        static int hHook = 0;
        static readonly string HOOK_FILE = @"hook.tmp";
        public const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private const int VK_DELETE = 0x2E;
        private const int VK_CONTROL = 0x11;
        private const int VK_MENU = 0x12;
        private const int VK_TAB = 0x09;
        private const int WM_SYSKEYDOWN = 0x0104;
        HookProc KeyBoardHookProcedure;
        [StructLayout(LayoutKind.Sequential)]
        public class KeyBoardHookStruct
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }
        [DllImport("user32.dll")]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);
        [DllImport("user32.dll")]
        public static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
        [DllImport("kernel32.dll")]
        public static extern IntPtr GetModuleHandle(string name);

        // 声明GetKeyState函数
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern short GetKeyState(int nVirtKey);

        public int Start()
        {
            // 安装键盘钩子 
            // 关闭之前的hook

            if (hHook == 0)
            {
                KeyBoardHookProcedure = new HookProc(KeyBoardHookProc);
                // 为什么hook  https://www.jianshu.com/p/513d4893248d
                // https://blog.csdn.net/changtianshuiyue/article/details/9316999
                // 检查你的项目是不是 窗口应用程序 （控制台程序不行），回调函数最好在主窗体的类中。
                // 原因似乎因为全局钩子回调需要寄宿在一个线程上，但是控制台应用程序中获得的当前实例（IntPtr hInstance）
                // 或许并不是当前程序的实例，而是一个控制台窗口的实例，回调的时候当然找不到回调函数了。
                // 在form的初始化时调用 startHook(); 是可以起作用的

                // 在新的线程中使用Application.Run来启动消息循环

                hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyBoardHookProcedure, GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);

                //hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyBoardHookProcedure, GetModuleHandle(Process.GetProcessesByName("bwexam")[0].MainModule.ModuleName), 0);

                //hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyBoardHookProcedure, GetModuleHandle(null), 0);

                //if (hHook == 0)
                //    Close();
                //else
                //{
                //    RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\System", true);
                //    if (key == null)//如果该项不存在的话，则创建该项
                //        key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\System");
                //    key.SetValue("DisableTaskMgr", 1, RegistryValueKind.DWord);
                //    key.Close();
                //}
                try
                {
                    using (FileStream fs = File.Create(HOOK_FILE))
                    {
                        byte[] info = new UTF8Encoding(true).GetBytes(hHook.ToString());
                        fs.Write(info, 0, info.Length);
                    }
                }
                catch
                {
                }
            }
            return hHook;
        }



        public int Close()
        {



            if (hHook != 0)
            {
                bool success = UnhookWindowsHookEx(hHook);
                if (!success)
                {
                    hHook = 0;
                }

            }

            try
            {
                if (File.Exists(HOOK_FILE))
                {
                    string hookidStr = File.ReadAllText(HOOK_FILE);
                    UnhookWindowsHookEx(int.Parse(hookidStr));
                    File.Delete(HOOK_FILE);
                    hHook = 0;
                }
            }
            catch
            {
            }




            return hHook;
            //如果去掉钩子失败. 
            //if (!retKeyboard) throw new Exception("UnhookWindowsHookEx failed.");
            //RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Policies\System", true);
            //if (key != null)
            //{
            //    key.DeleteValue("DisableTaskMgr", false);
            //    key.Close();
            //}
        }
        public static int KeyBoardHookProc(int nCode, int wParam, IntPtr lParam)
        {




            if (nCode >= 0 && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
            {


                int vkCode = Marshal.ReadInt32(lParam);

                if (vkCode == 91) // 截获左win(开始菜单键) 
                {
                    //Console.WriteLine("left win");
                    return 1;
                }

                if (vkCode == (int)Keys.Tab && (Control.ModifierKeys & Keys.Alt) == Keys.Alt)
                {
                    //Console.WriteLine("alt+tab3 ");
                    return 1; // 返回 1 表示拦截该消息
                }

               
                if (vkCode == 92)// 截获右win 
                {
                    //Console.WriteLine("right win");
                    return 1;
                }
            }

            return CallNextHookEx(hHook, nCode, wParam, lParam);
        }

        public void Dispose()
        {
            Close();
            GC.SuppressFinalize(this);
        }





    }

    public class WebSocketServer
    {

        private WebSocket currentSocket;
        private volatile bool closed = false;
        HttpListener listener;
        private long lastMsgTime;



        //消息处理器
        private readonly Func<string, string> messageHandler;

        public WebSocketServer(Func<string, string> messageHandler)
        {
            this.messageHandler = messageHandler;

        }

        public WebSocketServer()
        {
            this.messageHandler = (string msg) => { Console.WriteLine($"Received message: {msg}"); return msg; };

        }

        public long getLastMsgTime()
        {
            return lastMsgTime;
        }




        public async Task StartServer(string ipAddress, int port)
        {
            listener = new HttpListener();
            listener.Prefixes.Add($"http://{ipAddress}:{port}/550eXUE8400eXUE29b41d4a6444uXUEiofnxc/20240718/");
            listener.Start();
            this.closed = false;
            //Console.WriteLine("Server started. Waiting for connections...");
            this.lastMsgTime = DateTime.Now.Ticks;
            while (!this.closed)
            {
                HttpListenerContext context = await listener.GetContextAsync();
                if (context.Request.IsWebSocketRequest)
                {

                    _ = ProcessWebSocketRequest(context);
                }
                else
                {
                    context.Response.StatusCode = 400;
                    context.Response.Close();
                }
            }
            if (null != listener)
            {
                listener.Stop();

            }
            Console.WriteLine("=========Server stoped.");

        }

        private async Task ProcessWebSocketRequest(HttpListenerContext context)
        {
            HttpListenerWebSocketContext webSocketContext = await context.AcceptWebSocketAsync(null);
            WebSocket socket = webSocketContext.WebSocket;
            currentSocket = socket;

            // Handle incoming messages
            byte[] buffer = new byte[1024];
            while (socket.State == WebSocketState.Open)
            {
                if (this.closed)
                {
                    await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
                    this.currentSocket = null;
                }
                else
                {

                    WebSocketReceiveResult reciveResult = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                    this.lastMsgTime = DateTime.Now.Ticks;
                    if (reciveResult.MessageType == WebSocketMessageType.Text)
                    {
                        string receivedMessage = System.Text.Encoding.UTF8.GetString(buffer, 0, reciveResult.Count);
                        // 调用消息处理器
                        string result = this.messageHandler.Invoke(receivedMessage);
                        byte[] resultBytes = System.Text.Encoding.UTF8.GetBytes(result);
                        // Echo back the received message
                        await socket.SendAsync(new ArraySegment<byte>(resultBytes, 0, resultBytes.Length), WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                    else if (reciveResult.MessageType == WebSocketMessageType.Close)
                    {
                        await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
                    }
                }
            }
        }

        public async Task SendMessage(string msg)
        {
            WebSocket socket = currentSocket;

            if (null != socket && socket.State == WebSocketState.Open)
            {
                byte[] buffer = Encoding.UTF8.GetBytes(msg);
                // Echo back the received message
                await socket.SendAsync(new ArraySegment<byte>(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None);

            }
            else
            {
                Console.WriteLine("没有客户端连接到ws");
            }
        }

        public bool Stop()
        {
            this.closed = true;
            if (null != listener)
            {
                listener.Stop();

            }

            return true;
        }

    }

    public class WebSocketClient
    {
        public async Task ConnectToServer(string serverUri)
        {
            ClientWebSocket clientWebSocket = new ClientWebSocket();
            await clientWebSocket.ConnectAsync(new Uri(serverUri), CancellationToken.None);

            Console.WriteLine("Connected to the server. Start sending messages...");

            // Send messages to the server
            string message = "Hello, WebSocket!";
            byte[] buffer = Encoding.UTF8.GetBytes(message);
            await clientWebSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);

            // Receive messages from the server
            byte[] receiveBuffer = new byte[1024];
            while (clientWebSocket.State == WebSocketState.Open)
            {
                WebSocketReceiveResult result = await clientWebSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                if (result.MessageType == WebSocketMessageType.Text)
                {
                    string receivedMessage = Encoding.UTF8.GetString(receiveBuffer, 0, result.Count);
                    Console.WriteLine($"Received message from server: {receivedMessage}");
                }
            }
        }
    }

    /// <summary>
    ///  websocket 消息
    /// </summary>
    public class WsMessage
    {
        public string Id { get; set; }
        public string Kind { get; set; }
        public string Content { get; set; }
        public bool Success { get; set; }


        public WsMessage()
        {
            //Id = Guid.NewGuid().ToString();
            Kind = "reply";
            Success = true;
        }
    }
}
